import * as fs from 'fs';
import * as path from 'path';
import { ApiEndpoint, ApiParameter, ApiExample } from '../extension';

export class JavaScriptParser {
  async parseFile(filePath: string): Promise<ApiEndpoint[]> {
    const content = fs.readFileSync(filePath, 'utf8');
    const lines = content.split('\n');
    const endpoints: ApiEndpoint[] = [];

    for (let i = 0; i < lines.length; i++) {
      const line = lines[i].trim();
      
      // Look for Express.js/NestJS patterns
      if (line.includes('app.get(') || 
          line.includes('app.post(') || 
          line.includes('app.put(') || 
          line.includes('app.delete(') ||
          line.includes('app.patch(') ||
          line.includes('router.get(') ||
          line.includes('router.post(') ||
          line.includes('router.put(') ||
          line.includes('router.delete(') ||
          line.includes('router.patch(') ||
          line.includes('@Get(') ||
          line.includes('@Post(') ||
          line.includes('@Put(') ||
          line.includes('@Delete(') ||
          line.includes('@Patch(')) {
        
        const endpoint = await this.parseEndpoint(lines, i, filePath);
        if (endpoint) {
          endpoints.push(endpoint);
        }
      }
    }

    return endpoints;
  }

  private async parseEndpoint(lines: string[], startLine: number, filePath: string): Promise<ApiEndpoint | null> {
    const line = lines[startLine];
    let method = 'GET';
    let path = '';
    let description = '';
    let parameters: ApiParameter[] = [];
    let returnType = 'Object';
    let returnDescription = '';

    // Extract HTTP method and path
    if (line.includes('app.get(') || line.includes('router.get(') || line.includes('@Get(')) {
      method = 'GET';
      path = this.extractPath(line);
    } else if (line.includes('app.post(') || line.includes('router.post(') || line.includes('@Post(')) {
      method = 'POST';
      path = this.extractPath(line);
    } else if (line.includes('app.put(') || line.includes('router.put(') || line.includes('@Put(')) {
      method = 'PUT';
      path = this.extractPath(line);
    } else if (line.includes('app.delete(') || line.includes('router.delete(') || line.includes('@Delete(')) {
      method = 'DELETE';
      path = this.extractPath(line);
    } else if (line.includes('app.patch(') || line.includes('router.patch(') || line.includes('@Patch(')) {
      method = 'PATCH';
      path = this.extractPath(line);
    }

    // Look for function definition in next few lines
    let functionName = '';
    for (let i = startLine + 1; i < Math.min(startLine + 5, lines.length); i++) {
      const funcLine = lines[i];
      if (funcLine.includes('function') || funcLine.includes('=>') || funcLine.includes('(req, res)')) {
        functionName = this.extractFunctionName(funcLine, startLine);
        parameters = this.extractJavaScriptParameters(lines, i, funcLine);
        break;
      }
    }

    // Look for JSDoc comments above the route
    for (let i = startLine - 1; i >= Math.max(0, startLine - 10); i--) {
      const commentLine = lines[i];
      if (commentLine.includes('/**')) {
        description = this.extractJSDocDescription(lines, i);
        break;
      }
    }

    if (!functionName) {
      functionName = `${method.toLowerCase()}Handler`;
    }

    const examples = this.generateExamples(method, path, parameters, returnType);

    return {
      name: functionName,
      path,
      method,
      description: description || `${method} endpoint for ${functionName}`,
      parameters,
      returnType,
      returnDescription,
      examples,
      filePath,
      lineNumber: startLine + 1
    };
  }

  private extractPath(line: string): string {
    const pathMatch = line.match(/["']([^"']+)["']/);
    return pathMatch ? pathMatch[1] : '/';
  }

  private extractFunctionName(line: string, startLine: number): string {
    const functionMatch = line.match(/function\s+(\w+)/);
    if (functionMatch) {
      return functionMatch[1];
    }

    const arrowMatch = line.match(/const\s+(\w+)\s*=/);
    if (arrowMatch) {
      return arrowMatch[1];
    }

    const varMatch = line.match(/var\s+(\w+)\s*=/);
    if (varMatch) {
      return varMatch[1];
    }

    const letMatch = line.match(/let\s+(\w+)\s*=/);
    if (letMatch) {
      return letMatch[1];
    }

    return `handler_${startLine}`;
  }

  private extractJavaScriptParameters(lines: string[], funcLineIndex: number, funcLine: string): ApiParameter[] {
    const params: ApiParameter[] = [];
    
    // Common Express.js parameters
    params.push({
      name: 'req',
      type: 'Request',
      description: 'Express request object',
      required: true,
      source: 'request'
    });

    params.push({
      name: 'res',
      type: 'Response',
      description: 'Express response object',
      required: true,
      source: 'response'
    });

    // Look for additional parameters in function signature
    const paramMatch = funcLine.match(/\(([^)]+)\)/);
    if (paramMatch) {
      const paramString = paramMatch[1];
      const paramList = paramString.split(',').map(p => p.trim());
      
      for (const param of paramList) {
        if (param && param !== 'req' && param !== 'res' && param !== '') {
          const paramInfo = this.parseJavaScriptParameterInfo(param, lines, funcLineIndex);
          if (paramInfo) {
            params.push(paramInfo);
          }
        }
      }
    }
    
    return params;
  }

  private parseJavaScriptParameterInfo(paramString: string, lines: string[], funcLineIndex: number): ApiParameter | null {
    // Extract parameter name and type annotation (for TypeScript)
    const paramMatch = paramString.match(/^(\w+)(?:\s*:\s*([^=]+))?(?:\s*=\s*(.+))?$/);
    if (!paramMatch) return null;

    const name = paramMatch[1];
    const type = paramMatch[2] ? paramMatch[2].trim() : 'any';
    const defaultValue = paramMatch[3] ? paramMatch[3].trim() : undefined;

    // Determine parameter source and extract additional info
    let source = 'body';
    let description = `Parameter ${name} of type ${type}`;
    let required = !defaultValue;

    // Check for NestJS decorators or common patterns
    if (type.includes('@Param(') || type.includes('@Param()')) {
      source = 'path';
      const paramMatch = type.match(/@Param\(["']([^"']+)["']\)/);
      if (paramMatch) {
        description = `Path parameter: ${paramMatch[1]}`;
      } else {
        description = `Path parameter: ${name}`;
      }
    } else if (type.includes('@Query(') || type.includes('@Query()')) {
      source = 'query';
      const queryMatch = type.match(/@Query\(["']([^"']+)["']\)/);
      if (queryMatch) {
        description = `Query parameter: ${queryMatch[1]}`;
      } else {
        description = `Query parameter: ${name}`;
      }
    } else if (type.includes('@Body(') || type.includes('@Body()')) {
      source = 'body';
      const bodyMatch = type.match(/@Body\(([^)]+)\)/);
      if (bodyMatch) {
        description = `Request body: ${bodyMatch[1]}`;
      } else {
        description = `Request body: ${name}`;
      }
    } else if (type.includes('@Headers(') || type.includes('@Headers()')) {
      source = 'header';
      const headerMatch = type.match(/@Headers\(["']([^"']+)["']\)/);
      if (headerMatch) {
        description = `Header: ${headerMatch[1]}`;
      } else {
        description = `Header: ${name}`;
      }
    } else if (type.includes('@Session(') || type.includes('@Session()')) {
      source = 'session';
      description = `Session parameter: ${name}`;
    } else if (type.includes('@UploadedFile(') || type.includes('@UploadedFile()')) {
      source = 'file';
      description = `File upload: ${name}`;
    }

    // Look for parameter documentation in JSDoc
    const paramDoc = this.extractParameterDocumentation(lines, funcLineIndex, name);
    if (paramDoc) {
      description = paramDoc;
    }

    return {
      name,
      type,
      description,
      required,
      defaultValue,
      source
    };
  }

  private extractParameterDocumentation(lines: string[], funcLineIndex: number, paramName: string): string | null {
    // Look for @param documentation in JSDoc above the function
    for (let i = funcLineIndex - 1; i >= Math.max(0, funcLineIndex - 20); i--) {
      const line = lines[i];
      if (line.includes('/**')) {
        // Search through the JSDoc block
        for (let j = i; j < lines.length; j++) {
          const docLine = lines[j];
          if (docLine.includes('*/')) break;
          
          const paramMatch = docLine.match(new RegExp(`@param\\s+{([^}]+)}\\s+${paramName}\\s+(.+)`, 'i'));
          if (paramMatch) {
            return paramMatch[2].trim();
          }
          
          const paramMatch2 = docLine.match(new RegExp(`@param\\s+${paramName}\\s+(.+)`, 'i'));
          if (paramMatch2) {
            return paramMatch2[1].trim();
          }
        }
        break;
      }
    }
    return null;
  }

  private extractJSDocDescription(lines: string[], startLine: number): string {
    let description = '';
    
    for (let i = startLine; i < lines.length; i++) {
      const line = lines[i];
      if (line.includes('*/')) break;
      
      if (line.includes('*')) {
        const content = line.replace(/^\s*\*\s*/, '').trim();
        if (content && !content.startsWith('@')) {
          description += content + ' ';
        }
      }
    }
    
    return description.trim();
  }

  private generateExamples(method: string, path: string, parameters: ApiParameter[], returnType: string): ApiExample[] {
    const examples: ApiExample[] = [];

    // cURL example
    let curlExample = `curl -X ${method} "http://localhost:3000${path}"`;
    
    // Add headers if needed
    const headers: string[] = [];
    const bodyParams: ApiParameter[] = [];
    const queryParams: ApiParameter[] = [];
    const pathParams: ApiParameter[] = [];

    parameters.forEach(param => {
      if (param.source === 'header') {
        headers.push(`-H "${param.name}: value"`);
      } else if (param.source === 'body') {
        bodyParams.push(param);
      } else if (param.source === 'query') {
        queryParams.push(param);
      } else if (param.source === 'path') {
        pathParams.push(param);
      }
    });

    // Add query parameters
    if (queryParams.length > 0) {
      const queryString = queryParams.map(p => `${p.name}=value`).join('&');
      curlExample = curlExample.replace('"', `"?${queryString}"`);
    }

    // Add headers
    if (headers.length > 0) {
      curlExample += ` \\\n  ${headers.join(' \\\n  ')}`;
    }

    // Add body for POST/PUT/PATCH
    if (method !== 'GET' && bodyParams.length > 0) {
      curlExample += ` \\\n  -H "Content-Type: application/json" \\\n  -d '{\n`;
      const paramData = bodyParams.map(p => `    "${p.name}": "value"`).join(',\n');
      curlExample += paramData + '\n  }';
    }

    examples.push({
      title: 'cURL Request',
      description: 'Example cURL request for this endpoint',
      code: curlExample,
      language: 'bash'
    });

    // JavaScript fetch example
    let jsExample = `// Using fetch API\n`;
    
    if (method === 'GET') {
      jsExample += `fetch("http://localhost:3000${path}")\n  .then(response => response.json())\n  .then(data => console.log(data));`;
    } else {
      jsExample += `fetch("http://localhost:3000${path}", {\n  method: "${method}",\n  headers: {\n    "Content-Type": "application/json",\n  },\n`;
      
      if (bodyParams.length > 0) {
        jsExample += `  body: JSON.stringify({\n`;
        const paramData = bodyParams.map(p => `    ${p.name}: "value"`).join(',\n');
        jsExample += paramData + '\n  })\n';
      }
      jsExample += `})\n  .then(response => response.json())\n  .then(data => console.log(data));`;
    }

    examples.push({
      title: 'JavaScript Fetch',
      description: 'Example using JavaScript fetch API',
      code: jsExample,
      language: 'javascript'
    });

    return examples;
  }
} 